#ifndef TCP_LISTENER_H
#define TCP_LISTENER_H

#include <memory>
#include <queue>
#include <mutex>
#include <thread>

#include "endpoint_addr.h"
#include "tcp_connection.h"

#ifndef NATIVE_SOCKET
#include "framebuf_mgr.h"
#endif

namespace usnet {
class  TCPListener {
public:
    ~TCPListener();

    static std::unique_ptr<TCPListener> MakeUnique(EndpointAddr* addr);

    inline std::unique_ptr<TCPConnection> Accept()
    {
        std::lock_guard<std::mutex> lock { mtx_ };
        if (pending_conn_.size() == 0) {
            return nullptr;
        }
        std::unique_ptr<TCPConnection> conn = std::move(pending_conn_.front());
        pending_conn_.pop();
        return std::move(conn);
    }

private:
    TCPListener(EndpointAddr* addr):addr_ { addr }
    {
        // nothing to do
    }
    inline void StartListen()
    {
        listener_ = std::thread(&TCPListener::Listen, this);
    }
    bool InitResource();
    void Listen();
#ifndef NATIVE_SOCKET
    void HandleRX(const uint32_t fb_id, const uint32_t len);
#endif

    bool stop_ { false };
#ifndef NATIVE_SOCKET
    const uint32_t max_pending_conn_ { 100 };
    const uint32_t nr_rx_fb_ { 100 };
    const uint32_t nr_tx_fb_ { 0 };
    EFResource ef_res_;
    std::shared_ptr<FrameBufMgr> fb_mgr_ { nullptr };
#else
    int listenfd_ { -1 };
#endif
    EndpointAddr* addr_ { nullptr };
    std::mutex mtx_ { };
    std::queue<std::unique_ptr<TCPConnection>> pending_conn_ { };
    std::thread listener_ { };
};
} // end of namespace usent
#endif